"""
SkyWalking 数据采集器
重构版本：返回结构化数据，移除打印输出，支持重试和错误处理
"""

import requests
import json
from datetime import datetime, timedelta, timezone
from typing import Dict, List, Optional, Any, Tuple
import time


class SkyWalkingCollector:
    """SkyWalking 数据采集器类"""
    
    def __init__(
        self, 
        skywalking_url: str,
        timeout: int = 30,
        max_retries: int = 3,
        retry_delay: int = 2
    ):
        """
        初始化采集器
        
        Args:
            skywalking_url: SkyWalking GraphQL API 地址
            timeout: 请求超时时间（秒）
            max_retries: 最大重试次数
            retry_delay: 重试延迟（秒）
        """
        self.skywalking_url = skywalking_url
        self.timeout = timeout
        self.max_retries = max_retries
        self.retry_delay = retry_delay
    
    def _get_time_range(self, minutes: int = 30) -> Dict[str, str]:
        """
        获取查询时间范围（使用 UTC 时间）
        
        Args:
            minutes: 时间范围（分钟）
        
        Returns:
            Dict: 包含 start, end, step 的字典
        """
        end_time = datetime.now(timezone.utc)
        start_time = end_time - timedelta(minutes=minutes)
        
        return {
            "start": start_time.strftime("%Y-%m-%d %H%M"),
            "end": end_time.strftime("%Y-%m-%d %H%M"),
            "step": "MINUTE"
        }
    
    def _graphql_query(self, query: str, variables: Optional[Dict] = None) -> Optional[Dict]:
        """
        执行 GraphQL 查询（带重试机制）
        
        Args:
            query: GraphQL 查询字符串
            variables: 查询变量
        
        Returns:
            Dict: 查询结果数据，失败返回 None
        """
        payload = {"query": query}
        if variables:
            payload["variables"] = variables
        
        last_error = None
        for attempt in range(self.max_retries):
            try:
                response = requests.post(
                    self.skywalking_url,
                    json=payload,
                    headers={"Content-Type": "application/json"},
                    timeout=self.timeout
                )
                
                if response.status_code != 200:
                    last_error = f"HTTP {response.status_code}: {response.text[:200]}"
                    if attempt < self.max_retries - 1:
                        time.sleep(self.retry_delay)
                        continue
                    return None
                
                data = response.json()
                
                if "errors" in data:
                    last_error = f"GraphQL errors: {json.dumps(data['errors'])}"
                    return None
                
                return data.get("data")
            
            except requests.exceptions.Timeout:
                last_error = f"Request timeout after {self.timeout}s"
                if attempt < self.max_retries - 1:
                    time.sleep(self.retry_delay)
                    continue
            except requests.exceptions.RequestException as e:
                last_error = f"Request failed: {str(e)}"
                if attempt < self.max_retries - 1:
                    time.sleep(self.retry_delay)
                    continue
            except Exception as e:
                last_error = f"Unexpected error: {str(e)}"
                return None
        
        # 所有重试失败后返回 None
        return None
    
    def get_all_services(self, time_range_minutes: int = 30) -> List[Dict[str, str]]:
        """
        获取所有服务
        
        Args:
            time_range_minutes: 时间范围（分钟）
        
        Returns:
            List[Dict]: 服务列表，格式: [{'id': 'xxx', 'name': 'xxx'}, ...]
        """
        time_range = self._get_time_range(time_range_minutes)
        query = f"""
        query {{
            services: getAllServices(duration: {{start: "{time_range['start']}", end: "{time_range['end']}", step: {time_range['step']}}}) {{
                id
                name
            }}
        }}
        """
        
        data = self._graphql_query(query)
        if data and data.get("services"):
            return data["services"]
        return []
    
    def get_service_instances(self, service_id: str, time_range_minutes: int = 30) -> List[Dict]:
        """
        获取服务实例
        
        Args:
            service_id: 服务 ID
            time_range_minutes: 时间范围（分钟）
        
        Returns:
            List[Dict]: 实例列表
        """
        time_range = self._get_time_range(time_range_minutes)
        query = """
        query queryInstances($serviceId: ID!, $duration: Duration!) {
            pods: listInstances(duration: $duration, serviceId: $serviceId) {
                id
                name
                language
                instanceUUID
                attributes {
                    name
                    value
                }
            }
        }
        """
        variables = {
            "serviceId": service_id,
            "duration": time_range
        }
        
        data = self._graphql_query(query, variables)
        if data and data.get("pods"):
            return data["pods"]
        return []
    
    def get_service_endpoints(self, service_id: str, limit: int = 100) -> List[Dict[str, str]]:
        """
        获取服务的所有端点
        
        Args:
            service_id: 服务 ID
            limit: 最大返回数量
        
        Returns:
            List[Dict]: 端点列表，格式: [{'id': 'xxx', 'name': 'xxx'}, ...]
        """
        query = """
        query queryEndpoints($serviceId: ID!, $keyword: String!, $limit: Int!) {
            pods: findEndpoint(serviceId: $serviceId, keyword: $keyword, limit: $limit) {
                id
                name
            }
        }
        """
        variables = {
            "serviceId": service_id,
            "keyword": "",
            "limit": limit
        }
        
        data = self._graphql_query(query, variables)
        if data and data.get("pods"):
            return data["pods"]
        return []
    
    def get_service_traces(
        self, 
        service_id: str, 
        time_range_minutes: int = 30,
        page_size: int = 100
    ) -> List[Dict]:
        """
        获取服务的 Trace 记录
        
        Args:
            service_id: 服务 ID
            time_range_minutes: 时间范围（分钟）
            page_size: 每页数量
        
        Returns:
            List[Dict]: Trace 列表
        """
        time_range = self._get_time_range(time_range_minutes)
        query = """
        query queryTraces($condition: TraceQueryCondition) {
            data: queryBasicTraces(condition: $condition) {
                traces {
                    key: segmentId
                    endpointNames
                    duration
                    start
                    isError
                    traceIds
                }
            }
        }
        """
        variables = {
            "condition": {
                "queryDuration": time_range,
                "paging": {
                    "pageNum": 1,
                    "pageSize": page_size
                },
                "traceState": "ALL",
                "queryOrder": "BY_START_TIME",
                "minTraceDuration": None,
                "maxTraceDuration": None,
                "serviceId": service_id
            }
        }
        
        data = self._graphql_query(query, variables)
        if data and data.get("data") and data["data"].get("traces"):
            return data["data"]["traces"]
        return []
    
    def get_service_metric(
        self, 
        metric_name: str, 
        service_name: str,
        time_range_minutes: int = 30
    ) -> Optional[Dict]:
        """
        获取服务指标数据
        
        Args:
            metric_name: 指标名称，如 'service_sla', 'service_cpm', 'service_resp_time'
            service_name: 服务名称
            time_range_minutes: 时间范围（分钟）
        
        Returns:
            Dict: 指标数据，包含 label 和 values
        """
        time_range = self._get_time_range(time_range_minutes)
        query = f"""
        query {{
            metrics: readMetricsValues(condition: {{
                name: "{metric_name}"
                entity: {{
                    scope: Service
                    serviceName: "{service_name}"
                    normal: true
                }}
            }}, duration: {{start: "{time_range['start']}", end: "{time_range['end']}", step: {time_range['step']}}}) {{
                label
                values {{
                    values {{
                        id
                        value
                    }}
                }}
            }}
        }}
        """
        
        data = self._graphql_query(query)
        if data and data.get("metrics"):
            return data["metrics"]
        return None
    
    def get_endpoint_metric(
        self,
        metric_name: str,
        endpoint_name: str,
        time_range_minutes: int = 30
    ) -> Optional[Dict]:
        """
        获取端点指标数据
        
        Args:
            metric_name: 指标名称，如 'endpoint_cpm', 'endpoint_resp_time'
            endpoint_name: 端点名称
            time_range_minutes: 时间范围（分钟）
        
        Returns:
            Dict: 指标数据
        """
        time_range = self._get_time_range(time_range_minutes)
        query = f"""
        query {{
            metrics: readMetricsValues(condition: {{
                name: "{metric_name}"
                entity: {{
                    scope: Endpoint
                    endpointName: "{endpoint_name}"
                    normal: true
                }}
            }}, duration: {{start: "{time_range['start']}", end: "{time_range['end']}", step: {time_range['step']}}}) {{
                label
                values {{
                    values {{
                        id
                        value
                    }}
                }}
            }}
        }}
        """
        
        data = self._graphql_query(query)
        if data and data.get("metrics"):
            return data["metrics"]
        return None
    
    @staticmethod
    def extract_metric_time_series(metric_data: Optional[Dict]) -> List[Tuple[datetime, float]]:
        """
        从指标数据中提取时间序列数据（时间戳+值）
        
        SkyWalking 10.x 返回格式:
        {
          "label": null,
          "values": {
            "values": [
              {"id": "metric_name_202511240241_serviceId", "value": 123}
            ]
          }
        }
        
        Args:
            metric_data: 指标数据字典
        
        Returns:
            List[Tuple[datetime, float]]: (timestamp, value) 元组列表
        """
        if not metric_data:
            return []
        
        from datetime import datetime
        
        time_series = []
        try:
            # SkyWalking 10.x 格式: metric_data['values']['values']
            values_container = metric_data.get('values')
            if not values_container:
                return []
            
            values_list = values_container.get('values')
            if not values_list:
                return []
            
            for v in values_list:
                if not isinstance(v, dict):
                    continue
                
                value = v.get('value')
                id_str = v.get('id')
                
                if value is None or id_str is None:
                    continue
                
                # 解析 ID 中的时间戳
                # 格式: "metric_name_YYYYMMDDHHmm_serviceId"
                # 例如: "service_resp_time_202511240241_MTAuNTUuMC4xMDc6MTQzMw==.1"
                try:
                    parts = str(id_str).split('_')
                    if len(parts) >= 2:
                        # 找到 12 位数字的时间戳部分
                        timestamp_str = None
                        for part in parts:
                            if len(part) == 12 and part.isdigit():
                                timestamp_str = part
                                break
                        
                        if timestamp_str:
                            # 解析为 YYYYMMDDHHmm 格式
                            ts = datetime.strptime(timestamp_str, "%Y%m%d%H%M")
                            time_series.append((ts, float(value)))
                except Exception as e:
                    import logging
                    logging.debug(f"Failed to parse timestamp from id '{id_str}': {e}")
                    continue
                    
        except Exception as e:
            import logging
            logging.warning(f"Error extracting metric time series: {e}")
        
        return time_series
    
    @staticmethod
    def extract_metric_values(metric_data: Optional[Dict]) -> List[float]:
        """
        从指标数据中提取值列表（仅值，不含时间戳）
        
        Args:
            metric_data: 指标数据字典
        
        Returns:
            List[float]: 值列表
        """
        if not metric_data:
            return []
        
        all_values = []
        try:
            if isinstance(metric_data, dict):
                if metric_data.get("values"):
                    for value_group in metric_data["values"]:
                        if isinstance(value_group, dict) and value_group.get("values"):
                            for v in value_group["values"]:
                                if isinstance(v, dict) and v.get("value") is not None:
                                    val = v["value"]
                                    if val > 0:  # 过滤无效值
                                        all_values.append(float(val))
            elif isinstance(metric_data, list):
                for item in metric_data:
                    if isinstance(item, dict) and item.get("values"):
                        for value_group in item["values"]:
                            if isinstance(value_group, dict) and value_group.get("values"):
                                for v in value_group["values"]:
                                    if isinstance(v, dict) and v.get("value") is not None:
                                        val = v["value"]
                                        if val > 0:
                                            all_values.append(float(val))
        except Exception:
            pass
        
        return all_values
    
    @staticmethod
    def calculate_average(values: List[float]) -> float:
        """计算平均值"""
        return sum(values) / len(values) if values else 0.0
    
    def collect_service_data(
        self,
        service_id: str,
        service_name: str,
        time_range_minutes: int = 30
    ) -> Dict[str, Any]:
        """
        采集单个服务的完整数据
        
        Args:
            service_id: 服务 ID
            service_name: 服务名称
            time_range_minutes: 时间范围（分钟）
        
        Returns:
            Dict: 包含服务所有数据的字典
        """
        result = {
            'service_id': service_id,
            'service_name': service_name,
            'collected_at': datetime.utcnow(),
            'time_range_minutes': time_range_minutes,
            'instances': [],
            'endpoints': [],
            'traces': [],
            'metrics': {},
            'errors': []
        }
        
        # 采集实例
        try:
            result['instances'] = self.get_service_instances(service_id, time_range_minutes)
        except Exception as e:
            result['errors'].append(f"Failed to collect instances: {str(e)}")
        
        # 采集端点
        try:
            result['endpoints'] = self.get_service_endpoints(service_id)
        except Exception as e:
            result['errors'].append(f"Failed to collect endpoints: {str(e)}")
        
        # 采集 Trace
        try:
            result['traces'] = self.get_service_traces(service_id, time_range_minutes)
        except Exception as e:
            result['errors'].append(f"Failed to collect traces: {str(e)}")
        
        # 采集关键指标
        metric_names = ['service_sla', 'service_cpm', 'service_resp_time', 'service_apdex']
        for metric_name in metric_names:
            try:
                metric_data = self.get_service_metric(metric_name, service_name, time_range_minutes)
                if metric_data:
                    values = self.extract_metric_values(metric_data)
                    result['metrics'][metric_name] = {
                        'raw_data': metric_data,
                        'values': values,
                        'average': self.calculate_average(values)
                    }
            except Exception as e:
                result['errors'].append(f"Failed to collect metric {metric_name}: {str(e)}")
        
        return result

